package com.gdust.hzx.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.gdust.hzx.cart.service.CartService;
import com.gdust.hzx.feign.product.ProductFeignClient;
import com.gdust.hzx.model.entity.h5.CartInfo;
import com.gdust.hzx.model.entity.product.ProductSku;
import com.gdust.hzx.utils.AuthContextUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * ClassName: CartServiceImpl
 * Package: com.gdust.hzx.cart.service.impl
 * Description:实现购物车服务接口CartService，用于处理购物车相关的业务逻辑。
 *
 * @Author syun
 * @Create 2024/12/27 15:23
 * @Version 1.0
 */
@Service
public class CartServiceImpl implements CartService {
    /**
     * 注入 RedisTemplate，用于与 Redis 进行交互
     */
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 注入远程调用的产品模块接口，用于获取商品信息
     */
    @Autowired
    private ProductFeignClient productFeignClient;

    /**
     * 获取当前用户的购物车的 Redis key
     *
     * @param userId 当前用户的 ID
     * @return 返回购物车对应的 Redis key
     */
    private String getCartKey(Long userId) {
        //定义key user:cart:userId
        // 购物车的 Redis key 格式：user:cart:userId
        return "user:cart:" + userId;
    }

    /**
     * 添加购物车
     * 思路：数据存储：为了提高对购物车数据操作的性能，可以使用Redis【HASH】存储购物车数据。
     *
     * @param skuId  商品sku的id值
     * @param skuNum 商品数量
     */
    //添加购物车
    @Override
    public void addToCart(Long skuId, Integer skuNum) {
        //1 必须登录状态，获取当前登录用户id（作为redis的hash类型的key值）
        //从ThreadLocal获取用户信息就可以了
        Long userId = AuthContextUtil.getUserInfo().getId();
        //构建hash类型key名称
        String cartKey = this.getCartKey(userId);

        //2 因为购物车放到redis里面
        //hash类型   key：userId   field：skuId   value：sku信息CartInfo
        //从redis里面获取购物车数据，根据用户id + skuId获取（hash类型key + field）
        Object cartInfoObj =
                redisTemplate.opsForHash().get(cartKey, String.valueOf(skuId));

        //3 如果购物车存在添加商品，把商品数量相加
        CartInfo cartInfo = null;
        if (cartInfoObj != null) { //添加到购物车商品已经存在的，把商品数量相加
            //cartInfoObj -- CartInfo
            cartInfo = JSON.parseObject(cartInfoObj.toString(), CartInfo.class);
            //数量相加
            cartInfo.setSkuNum(cartInfo.getSkuNum() + skuNum);
            //设置属性,购物车商品选中状态
            cartInfo.setIsChecked(1);
            //设置修改时间
            cartInfo.setUpdateTime(new Date());
        } else {
            //4 如果购物车没有添加商品，直接商品添加购物车（添加到redis里面）
            //远程调用实现：通过nacos + openFeign实现 根据skuId获取商品sku信息
            //创建一个新的 CartInfo 对象
            cartInfo = new CartInfo();

            //远程调用实现:根据skuId获取商品sku信息
            ProductSku productSku = productFeignClient.getBySkuId(skuId);
            //设置相关数据到cartInfo对象里面
            cartInfo.setCartPrice(productSku.getSalePrice());// 设置商品售价
            cartInfo.setSkuNum(skuNum);// 设置商品数量
            cartInfo.setSkuId(skuId);// 设置商品 SKU ID
            cartInfo.setUserId(userId);// 设置用户 ID
            cartInfo.setImgUrl(productSku.getThumbImg());// 设置商品缩略图
            cartInfo.setSkuName(productSku.getSkuName());// 设置商品名称
            cartInfo.setIsChecked(1);// 设置商品默认为选中状态
            cartInfo.setCreateTime(new Date()); // 设置创建时间
            cartInfo.setUpdateTime(new Date()); // 设置更新时间
        }

        //5.将商品信息保存到 Redis 中
        redisTemplate.opsForHash().put(cartKey,
                String.valueOf(skuId),
                JSON.toJSONString(cartInfo));
    }

    /**
     * 查询当前用户的购物车列表
     *
     * @return 返回购物车中所有商品的列表
     */
    @Override
    public List<CartInfo> getCartList() {
        //1.根据查询到的Redis里面的key值，根据当前的userId
        Long userId = AuthContextUtil.getUserInfo().getId();
        // 构建购物车的 Redis key
        String cartKey = this.getCartKey(userId);

        //2.根据key从Redis里面hash类型获取所有的value值 cartInfo
        List<Object> valueList = redisTemplate.opsForHash().values(cartKey);

        // 3. 将 Redis 返回的数据转换为 CartInfo 对象列表，并按创建时间排序
        // List<Object> -- List<CartInfo>
        // 检查从 Redis 获取到的值列表是否为空
        if (!CollectionUtils.isEmpty(valueList)) {
            // 使用流（stream）处理 Redis 返回的 valueList
            // 1.将每个值（Object）转换为 CartInfo 对象
            // 2.使用 sorted() 方法按创建时间（createTime）降序排列（最新的商品排前面）
            // 3.将排序后的结果收集为 List<CartInfo> 类型
            List<CartInfo> cartInfoList = valueList.stream()
                    // 通过 JSON.parseObject 将每个 Object 转换为 CartInfo 实体对象
                    .map(cartInfoObj -> JSON.parseObject(cartInfoObj.toString(), CartInfo.class))
                    // 使用 createTime 字段进行降序排序，最新加入的商品排在前面
                    .sorted((o1, o2) -> o2.getCreateTime().compareTo(o1.getCreateTime()))
                    // 将流中的元素收集到 List 中
                    .collect(Collectors.toList());
            return cartInfoList;  // 返回转换并排序后的购物车商品列表
        }


        // 如果购物车为空，返回一个空列表
        return new ArrayList<>();
    }

    /**
     * 删除购物车中的商品
     * <p>
     * 该方法通过商品的 SKU ID 从当前登录用户的购物车中删除对应的商品。
     *
     * @param skuId 商品的 SKU ID
     */
    @Override
    public void deleteCart(Long skuId) {
        //1.根据查询到的Redis里面的key值，根据当前的userId
        Long userId = AuthContextUtil.getUserInfo().getId();
        // 构建购物车的 Redis key
        String cartKey = this.getCartKey(userId);
        // 3. 调用 Redis 的 opsForHash().delete 方法删除购物车中的商品
        // 通过 Redis 的 Hash 类型删除指定字段（即删除该 skuId 对应的购物车商品）
        redisTemplate.opsForHash().delete(cartKey, String.valueOf(skuId));
    }

    /**
     * 更新购物车商品的选中状态
     * <p>
     * 该方法根据给定的商品 SKU ID 更新指定商品的选中状态。用户可以选择某个商品为选中或未选中状态。
     *
     * @param skuId     商品的 SKU ID
     * @param isChecked 是否选中，1 表示选中，0 表示未选中
     */
    @Override
    public void checkCart(Long skuId, Integer isChecked) {
        // 1. 获取当前登录用户的 ID（通过 ThreadLocal 获取当前用户会话信息）
        Long userId = AuthContextUtil.getUserInfo().getId();
        // 2. 构建该用户的购物车 Redis key，格式为：user:cart:userId
        String cartKey = this.getCartKey(userId);

        // 3. 检查 Redis 中是否存在该 SKU ID 对应的购物车商品信息
        // 使用 opsForHash().hasKey 检查 Redis Hash 是否包含指定的 SKU ID（即 field）
        Boolean hasKey = redisTemplate
                .opsForHash()
                .hasKey(cartKey, String.valueOf(skuId));

        // 4. 如果购物车中存在该商品，进行更新操作
        if (hasKey) {
            // 5. 从 Redis 获取该商品的 CartInfo 对象数据
            // 使用 opsForHash().get 获取指定 field 对应的 value（即商品的 CartInfo 信息）
            String cartInfoString =
                    redisTemplate
                            .opsForHash()
                            .get(cartKey, String.valueOf(skuId))
                            .toString();

            // 6. 将获取到的 JSON 字符串转换为 CartInfo 对象
            CartInfo cartInfo = JSON.parseObject(cartInfoString, CartInfo.class);

            // 7. 更新该商品的选中状态
            cartInfo.setIsChecked(isChecked);

            // 8. 将更新后的 CartInfo 对象保存回 Redis
            // 使用 opsForHash().put 将更新后的数据存回 Redis
            redisTemplate.opsForHash().put(cartKey,
                    String.valueOf(skuId),
                    JSON.toJSONString(cartInfo));

        }
    }

    /**
     * 更新购物车商品全部选中状态
     * 更新购物车商品的选中状态，设置所有商品为选中或取消选中状态
     * <p>
     * 该方法将所有商品的选中状态（`isChecked`）更新为给定值，用于全选或全不选操作。
     *
     * @param isChecked 是否选中，1表示选中，0表示未选中
     */
    @Override
    public void allCheckCart(Integer isChecked) {
        //1.构建查询Redis里面的key值，根据当前的userId
        Long userId = AuthContextUtil.getUserInfo().getId();
        // 2. 构建该用户的购物车 Redis key，格式为：user:cart:userId
        String cartKey = this.getCartKey(userId);

        // 3. 从 Redis 中获取该用户购物车的所有商品数据
        List<Object> objectList = redisTemplate.opsForHash().values(cartKey);


        // 4. 如果购物车中有商品数据，则进行后续操作
        if (!CollectionUtils.isEmpty(objectList)) {
            // 5. 将从 Redis 获取的 objectList 转换为 CartInfo 对象列表
            //List<Object>  --  List<CartInfo>
            List<CartInfo> cartInfoList =
                    // 将每个 object 转换为 CartInfo 对象
                    objectList.stream().map(object ->
                                    JSON.parseObject(object.toString(), CartInfo.class))
                            .collect(Collectors.toList());

            // 6. 遍历购物车商品列表，更新每个商品的选中状态
            cartInfoList.forEach(cartInfo -> {
                // 设置商品的选中状态
                cartInfo.setIsChecked(isChecked);
                // 将更新后的 CartInfo 对象重新保存回 Redis
                redisTemplate.opsForHash().put(cartKey, String.valueOf(cartInfo.getSkuId()),
                        JSON.toJSONString(cartInfo));
            });
        }
    }


    /**
     * 清空购物车
     */
    @Override
    public void clearCart() {
        //1 构建查询的redis里面key值，根据当前userId
        Long userId = AuthContextUtil.getUserInfo().getId();
        // 2. 构建该用户的购物车 Redis key，格式为：user:cart:userId
        String cartKey = this.getCartKey(userId);

        // 3. 根据key删除redis里面数据
        redisTemplate.delete(cartKey);
    }

    /**
     * 远程调用：订单结算使用，获取购物车选中商品列表
     * <p>
     * 该方法从 Redis 中获取当前用户购物车中所有选中的商品（即 `isChecked` 为 1 的商品）。
     * 该方法常用于订单结算，确保只处理用户选中的商品。
     *
     * @return 返回选中的商品列表，包含所有选中商品的详细信息
     */
    @Override
    public List<CartInfo> getAllChecked() {
        //1. 构建查询的 Redis 里面 key 值，根据当前的 userId
        Long userId = AuthContextUtil.getUserInfo().getId();  // 从当前用户的上下文获取 userId

        // 2. 构建该用户的购物车 Redis key，格式为：user:cart:userId
        String cartKey = this.getCartKey(userId);  // 通过 userId 获取该用户的购物车 key

        //3. 根据 key 获取购物车所有商品的列表（Redis 中的哈希值）
        List<Object> objectList = redisTemplate.opsForHash().values(cartKey);  // 获取所有的购物车商品信息

        //判断购物车中是否有商品
        if (!CollectionUtils.isEmpty(objectList)) {
            // 将 objectList 转换为 CartInfo 对象，并过滤出被选中的商品（isChecked == 1）
            List<CartInfo> cartInfoList = objectList.stream()// 对所有商品信息进行流式操作
                    .map(object -> JSON.parseObject(object.toString(), CartInfo.class))// 反序列化为 CartInfo 对象
                    .filter(cartInfo -> cartInfo.getIsChecked() == 1)// 过滤出选中的商品
                    .collect(Collectors.toList());// 收集为列表

            return cartInfoList;// 返回选中的商品列表
        }

        // 如果购物车为空，返回一个空列表
        return new ArrayList<>();
    }

    /**
     * 远程调用：删除生成订单的购物车商品
     */
    @Override
    public void deleteChecked() {
        // 1. 获取当前用户的 ID，并构建与用户相关的购物车 Redis key
        Long userId = AuthContextUtil.getUserInfo().getId(); // 从上下文获取当前用户的 ID
        String cartKey = this.getCartKey(userId); // 使用用户 ID 构建购物车的 Redis key

        // 2. 根据 key 获取购物车中所有商品的值，返回一个包含所有商品的 List
        List<Object> objectList = redisTemplate.opsForHash().values(cartKey); // 获取指定 cartKey 下所有商品的数据

        // 3. 筛选出被选中的商品，并从购物车中删除它们
        objectList.stream()
                // 将每个 Redis 存储的商品信息 (String) 解析成 CartInfo 对象
                .map(object -> JSON.parseObject(object.toString(), CartInfo.class))
                // 过滤出选中的商品（isChecked 为 1 的商品）
                .filter(cartInfo -> cartInfo.getIsChecked() == 1)
                // 删除购物车中被选中的商品，使用其 skuId 删除 Redis 中的该商品
                .forEach(cartInfo -> redisTemplate.opsForHash().delete(cartKey,
                        String.valueOf(cartInfo.getSkuId()))); // 删除 Redis 中的商品
    }



}
